 

package com.junjie.shortlink.project.toolkit;

import cn.hutool.core.lang.hash.MurmurHash;

/**
 * HASH 工具类
 *
 */
public class HashUtil {

    private static final char[] CHARS = new char[]{
            '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
            'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
            'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'
    };
    private static final int SIZE = CHARS.length;

    /**
     * Base62 编码是将数据转换为只包含数字和字母的一种方法。
     * 它使用了 62 个字符，分别是 0-9、a-z、A-Z，可以作为 URL 短链接、文件名等场景的字符串表示，
     * 相对于 16 进制或 64 进制等其他编码，Base62 具有更高的可读性和稳定性
     *
     * 假设我们使用的是 26 个字母的大小写，加上 10 个数字，那么对于短链接可以表示的最大组合数量为：
     * ●
     * N = 4，组合数为 62 ^ 4 = 14_776_336，1477 万左右。
     * ●
     * N = 5，组合数为 62 ^ 5 = 916_132_832，9.16 亿左右。
     * ●
     * N = 6，组合数为 62 ^ 6 = 56_800_235_584，568 亿左右。
     */
    private static String convertDecToBase62(long num) {
        StringBuilder sb = new StringBuilder();
        while (num > 0) {
            int i = (int) (num % SIZE);
            sb.append(CHARS[i]);
            num /= SIZE;
        }
        return sb.reverse().toString();
    }

    /**
     * 哈希函数将输入的数据映射为一个固定长度的哈希值，而不同的输入可能会映射为相同的哈希值，这被称为哈希冲突。
     * 在短链接生成过程中，原始长链接经过哈希函数进行计算，生成一个哈希值。如果两个不同的原始长链接经过哈希计算后得到相同的哈希值，那么它们将生成相同的短链接。
     * 这种情况通常是由于哈希函数的输出空间有限，而输入空间却是无限的。因此，无论哈希函数的设计有多好，仍然存在一定的概率会出现冲突。
     */
    public static String hashToBase62(String str) {
        // MurmurHash 是一种非加密型哈希函数，适用于一般的哈希检索操作。与其他流行的哈希函数相比，MurmurHash 在处理规律性较强的键时具有更好的随机分布特性。由于它是非加密型的，相比 MD5、SHA 等加密算法，MurmurHash 的性能要高得多（实际上是 MD5 等加密算法的十倍以上）
        // 转出来比较长，如 847257054
        int i = MurmurHash.hash32(str);
        // keypoint 注意事项，Hash 值可能有负数，通过负数进行 Base62 转换会得到空字符串，所以我们要进行判断
        long num = i < 0 ? Integer.MAX_VALUE - (long) i : i;
        // 转Base62，目的是更短一点
        return convertDecToBase62(num);
    }
}